package com.cens.mall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.cens.common.to.SeckillOrderTO;
import com.cens.common.to.mq.OrderTo;
import com.cens.common.utils.R;
import com.cens.common.vo.MemberEntityVo;
import com.cens.mall.order.constant.OrderConstant;
import com.cens.mall.order.entity.OrderItemEntity;
import com.cens.mall.order.entity.PaymentInfoEntity;
import com.cens.mall.order.enums.OrderStatusEnum;
import com.cens.mall.order.feign.CartFeignService;
import com.cens.mall.order.feign.CommodityFeignService;
import com.cens.mall.order.feign.MemberFeignService;
import com.cens.mall.order.feign.WareFeignService;
import com.cens.mall.order.intercepter.LoginIntercepter;
import com.cens.mall.order.service.OrderItemService;
import com.cens.mall.order.service.PaymentInfoService;
import com.cens.mall.order.to.OrderCreateTo;
import com.cens.mall.order.vo.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cens.common.utils.PageUtils;
import com.cens.common.utils.Query;

import com.cens.mall.order.dao.OrderDao;
import com.cens.mall.order.entity.OrderEntity;
import com.cens.mall.order.service.OrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    private ThreadLocal<OrderSubmitVo> submitVoThreadLocal = new ThreadLocal<>();
    @Autowired
    private OrderItemService orderItemService;
    @Autowired
    private CartFeignService cartFeignService;
    @Autowired
    private MemberFeignService memberFeignService;
    @Autowired
    private WareFeignService wareFeignService;
    @Autowired
    private CommodityFeignService commodityFeignService;
    @Autowired
    private PaymentInfoService paymentInfoService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private ThreadPoolExecutor executor;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        MemberEntityVo memberEntityVo = LoginIntercepter.loginUser.get();
        RequestAttributes mainRequestAttributes = RequestContextHolder.getRequestAttributes();
        //查询收货地址列表
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(
                () -> {
                    //共享request attributes
                    RequestContextHolder.setRequestAttributes(mainRequestAttributes);
                    List<MemberReceiveAddressVo> address = memberFeignService.getAddress(memberEntityVo.getId());
                    orderConfirmVo.setAddress(address);
                }, executor
        );
        //查询当前用户购物车选中的商品项
        CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(
                () -> {
                    //共享request attributes
                    RequestContextHolder.setRequestAttributes(mainRequestAttributes);
                    List<OrderItemVo> currentUserCartItems = cartFeignService.getCurrentUserCartItems();
                    orderConfirmVo.setItems(currentUserCartItems);
                    //订单总金额
                    BigDecimal total = currentUserCartItems.stream()
                            .map(item -> item.getPrice().multiply(new BigDecimal(item.getCount())))
                            .reduce(BigDecimal::add)
                            .orElse(BigDecimal.ZERO);
                    orderConfirmVo.setTotal(total);
                    orderConfirmVo.setPayPrice(total);
                }, executor
        ).thenRunAsync(
                () -> {
                    List<OrderItemVo> items = orderConfirmVo.getItems();
                    List<Long> skuIds = items.stream().map(OrderItemVo::getSkuId).collect(Collectors.toList());
                    R skusHasStock = wareFeignService.getSkusHasStock(skuIds);
                    List<SkuHasStockVo> data = skusHasStock.getData(new TypeReference<List<SkuHasStockVo>>() {
                    });
                    if (CollectionUtils.isNotEmpty(data)) {
                        orderConfirmVo.setStocks(
                                data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock))
                        );
                    }
                }, executor
        );

        //取用户积分信息
        orderConfirmVo.setIntegration(memberEntityVo.getIntegration());

        //TODO 防重令牌
        String token = UUID.randomUUID().toString().replace("-", "");
        orderConfirmVo.setOrderToken(token);
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberEntityVo.getId(), token, 30, TimeUnit.MINUTES);
        CompletableFuture.allOf(addressFuture, cartFuture).get();
        return orderConfirmVo;
    }

    @Override
//    @GlobalTransactional
    @Transactional(rollbackFor = Exception.class)
    public OrderSubmitResponseVo submitOrder(OrderSubmitVo orderSubmitVo) {
        submitVoThreadLocal.set(orderSubmitVo);
        OrderSubmitResponseVo responseVo = new OrderSubmitResponseVo();
        //验证令牌
        String orderToken = orderSubmitVo.getOrderToken();
        MemberEntityVo memberEntityVo = LoginIntercepter.loginUser.get();
        String redisKey = OrderConstant.USER_ORDER_TOKEN_PREFIX + memberEntityVo.getId();
        //使用redis lua 脚本 原子验证和删除令牌
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        Long tokenResult = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList(redisKey), orderToken);
        if (tokenResult == 0L) {
            //校验不通过
            responseVo.setCode(1);
            return responseVo;
        }
        //令牌检验通过
        //创建订单
        OrderCreateTo order = createOrder();
        //验证价格
        BigDecimal payAmount = order.getOrderEntity().getPayAmount();
        BigDecimal payPrice = orderSubmitVo.getPayPrice();
        if (Math.abs(payAmount.subtract(payPrice).doubleValue()) > 0.01) {
            //价格验证失败
            responseVo.setCode(2);
            return responseVo;
        }
        //保存订单信息
        saveOrder(order);
        //扣减(锁定)库存
        WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
        wareSkuLockVo.setOrderSn(order.getOrderEntity().getOrderSn());
        wareSkuLockVo.setLockItems(
                order.getItems().stream()
                        .map(item -> {
                            OrderItemVo orderItemVo = new OrderItemVo();
                            orderItemVo.setSkuId(item.getSkuId());
                            orderItemVo.setCount(item.getSkuQuantity());
                            orderItemVo.setTitle(item.getSkuName());
                            return orderItemVo;
                        }).collect(Collectors.toList())
        );
        R lockStockResult = wareFeignService.orderLockStock(wareSkuLockVo);
        if(lockStockResult.getCode() != 0){
            //锁定失败
            responseVo.setCode(3);
            return responseVo;
        }
        //订单创建成功，发送消息
        rabbitTemplate.convertAndSend(
                "order-event-exchange",
                "order.create.order",
                order
        );
        responseVo.setCode(0);
        responseVo.setOrder(order.getOrderEntity());
        return responseVo;
    }

    @Override
    public OrderEntity getOrderByOrderSn(String orderSn) {
        return this.getOne(
                new LambdaQueryWrapper<OrderEntity>()
                    .eq(OrderEntity::getOrderSn,orderSn)
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void closeOrder(OrderCreateTo order) {
        Long orderId = order.getOrderEntity().getId();
        OrderEntity orderEntity = this.getById(orderId);
        if(orderEntity.getStatus().equals(OrderStatusEnum.CREATE_NEW.getCode())){
            //关闭订单
            OrderEntity update = new OrderEntity();
            update.setId(orderEntity.getId());
            update.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(update);
            //发送消息 解锁库存
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(orderEntity,orderTo);
            rabbitTemplate.convertAndSend(
                    "order-event-exchange",
                    "order.release.other",
                    orderTo
            );
        }
    }

    @Override
    public PayVo getOrderPay(String orderSn) {
        OrderEntity orderEntity = this.getOrderByOrderSn(orderSn);
        PayVo payVo = new PayVo();
        payVo.setTotal_amount(orderEntity.getPayAmount().setScale(2, RoundingMode.UP).toString());
        payVo.setOut_trade_no(orderSn);
        List<OrderItemEntity> list = orderItemService.list(
                new LambdaQueryWrapper<OrderItemEntity>()
                        .eq(OrderItemEntity::getOrderSn, orderSn)
        );
        payVo.setSubject(list.get(0).getSkuName());
        payVo.setBody(list.get(0).getSkuAttrsVals());
        return payVo;
    }

    @Override
    public PageUtils queryPageWithItem(Map<String, Object> params) {
        MemberEntityVo memberEntityVo = LoginIntercepter.loginUser.get();
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new LambdaQueryWrapper<OrderEntity>()
                        .eq(OrderEntity::getMemberId, memberEntityVo.getId())
                        .orderByDesc(OrderEntity::getId)
        );
        List<OrderEntity> collect = page.getRecords().stream()
                .peek(
                        order -> {
                            List<OrderItemEntity> list = orderItemService.list(
                                    new LambdaQueryWrapper<OrderItemEntity>().eq(OrderItemEntity::getOrderSn, order.getOrderSn())
                            );
                            order.setOrderItemEntityList(list);
                        }
                ).collect(Collectors.toList());
        page.setRecords(collect);

        return new PageUtils(page);
    }

    @Override
    public String handlePayResult(PayAsyncVo vo) {
        //保存支付流水
        PaymentInfoEntity paymentInfoEntity = new PaymentInfoEntity();
        paymentInfoEntity.setAlipayTradeNo(vo.getTrade_no());
        paymentInfoEntity.setOrderSn(vo.getOut_trade_no());
        paymentInfoEntity.setPaymentStatus(vo.getTrade_status());
        paymentInfoEntity.setCallbackTime(vo.getNotify_time());
        paymentInfoService.save(paymentInfoEntity);
        if(vo.getTrade_status().equals("TRADE_SUCCESS") || vo.getTrade_status().equals("TRADE_FINISH")){
            String orderSn = vo.getOut_trade_no();
            this.update(
                    new LambdaUpdateWrapper<OrderEntity>()
                            .eq(OrderEntity::getOrderSn,orderSn)
                            .set(OrderEntity::getStatus,OrderStatusEnum.PAYED.getCode())
            );
        }
        return "success";
    }

    @Override
    public void createSeckillOrder(SeckillOrderTO order) {
        //保存订单信息
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(order.getOrderSn());
        orderEntity.setMemberId(order.getMemberId());
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setPayAmount(order.getSeckillPrice().multiply(new BigDecimal(order.getNum())));
        this.save(orderEntity);
        //保存订单项信息
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        orderItemEntity.setOrderSn(order.getOrderSn());
        orderItemEntity.setRealAmount(orderEntity.getPayAmount());
        orderItemEntity.setSkuQuantity(order.getNum());
        orderItemService.save(orderItemEntity);
    }

    private void saveOrder(OrderCreateTo order) {
        OrderEntity orderEntity = order.getOrderEntity();
        orderEntity.setModifyTime(new Date());
        this.save(orderEntity);
        orderItemService.saveBatch(order.getItems());
    }

    private OrderCreateTo createOrder() {
        OrderCreateTo orderCreateTo = new OrderCreateTo();
        //生成订单号
        String orderSn = IdWorker.getTimeId();
        //生成订单
        OrderEntity orderEntity = buildOrder(orderSn);
        //构建订单项
        List<OrderItemEntity> orderItemEntities = buildOrderItems(orderSn);
        //计算价格
        computePrice(orderEntity, orderItemEntities);
        //返回
        orderCreateTo.setOrderEntity(orderEntity);
        orderCreateTo.setItems(orderItemEntities);
        orderCreateTo.setFare(orderEntity.getFreightAmount());
        orderCreateTo.setPayPrice(orderEntity.getPayAmount());
        return orderCreateTo;
    }

    private void computePrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) {
        //订单总额
        orderEntity.setTotalAmount(
                orderItemEntities.stream()
                        .map(OrderItemEntity::getRealAmount)
                        .reduce(BigDecimal::add)
                        .orElse(BigDecimal.ZERO)
        );
        //应付总额
        orderEntity.setPayAmount(orderEntity.getTotalAmount().add(orderEntity.getFreightAmount()));
        //优惠金额
        orderEntity.setPromotionAmount(BigDecimal.ZERO);
        orderEntity.setCouponAmount(BigDecimal.ZERO);
        orderEntity.setDiscountAmount(BigDecimal.ZERO);
        orderEntity.setIntegrationAmount(BigDecimal.ZERO);
        //积分
        orderEntity.setIntegration(orderItemEntities.stream().mapToInt(OrderItemEntity::getGiftIntegration).sum());
        orderEntity.setGrowth(orderItemEntities.stream().mapToInt(OrderItemEntity::getGiftGrowth).sum());
    }

    private OrderEntity buildOrder(String orderSn) {
        OrderSubmitVo orderSubmitVo = submitVoThreadLocal.get();
        MemberEntityVo memberEntityVo = LoginIntercepter.loginUser.get();
        OrderEntity orderEntity = new OrderEntity();
        //会员id
        orderEntity.setMemberId(memberEntityVo.getId());
        //订单状态
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setOrderSn(orderSn);
        //1、获取收货地址和运费信息
        R data = wareFeignService.getFare(orderSubmitVo.getAddressId());
        FareVo fareVo = data.getData(new TypeReference<FareVo>() {
        });
        //运费
        orderEntity.setFreightAmount(fareVo.getFare());
        //收货人信息
        orderEntity.setReceiverCity(fareVo.getMemberReceiveAddressVo().getCity());
        orderEntity.setReceiverDetailAddress(fareVo.getMemberReceiveAddressVo().getDetailAddress());
        orderEntity.setReceiverName(fareVo.getMemberReceiveAddressVo().getName());
        orderEntity.setReceiverPhone(fareVo.getMemberReceiveAddressVo().getPhone());
        orderEntity.setReceiverPostCode(fareVo.getMemberReceiveAddressVo().getPostCode());
        orderEntity.setReceiverProvince(fareVo.getMemberReceiveAddressVo().getProvince());
        orderEntity.setReceiverRegion(fareVo.getMemberReceiveAddressVo().getRegion());
        return orderEntity;
    }

    private List<OrderItemEntity> buildOrderItems(String orderSn) {
        //2、获取所有的订单项信息
        List<OrderItemVo> currentUserCartItems = cartFeignService.getCurrentUserCartItems();
        if (CollectionUtils.isNotEmpty(currentUserCartItems)) {
            return currentUserCartItems.stream()
                    .map(
                            cartItem -> {
                                OrderItemEntity orderItemEntity = buildOrderItem(cartItem);
                                orderItemEntity.setOrderSn(orderSn);
                                return orderItemEntity;
                            }
                    ).collect(Collectors.toList());
        }
        return new ArrayList<>();
    }

    private OrderItemEntity buildOrderItem(OrderItemVo cartItem) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        //spu信息
        R spuInfoResult = commodityFeignService.spuInfoBySkuId(cartItem.getSkuId());
        SpuInfoVo spuInfoVo = spuInfoResult.getData(new TypeReference<SpuInfoVo>() {
        });
        orderItemEntity.setSpuId(spuInfoVo.getId());
        orderItemEntity.setSpuName(spuInfoVo.getSpuName());
        orderItemEntity.setSpuBrand(spuInfoVo.getBrandId().toString());
        orderItemEntity.setCategoryId(spuInfoVo.getCatalogId());
        //sku信息
        orderItemEntity.setSkuId(cartItem.getSkuId());
        orderItemEntity.setSkuName(cartItem.getTitle());
        orderItemEntity.setSkuPic(cartItem.getImage());
        orderItemEntity.setSkuPrice(cartItem.getPrice());
        orderItemEntity.setSkuQuantity(cartItem.getCount());
        orderItemEntity.setSkuAttrsVals(StringUtils.collectionToDelimitedString(cartItem.getSkuAttr(), ";"));
        // 积分
        orderItemEntity.setGiftGrowth(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());
        orderItemEntity.setGiftIntegration(cartItem.getPrice().multiply(new BigDecimal(cartItem.getCount())).intValue());
        //价格
        orderItemEntity.setPromotionAmount(BigDecimal.ZERO);
        orderItemEntity.setCouponAmount(BigDecimal.ZERO);
        orderItemEntity.setIntegrationAmount(BigDecimal.ZERO);
        orderItemEntity.setRealAmount(
                orderItemEntity.getSkuPrice()
                        .multiply(new BigDecimal(orderItemEntity.getSkuQuantity()))
                        .subtract(orderItemEntity.getPromotionAmount())
                        .subtract(orderItemEntity.getCouponAmount())
                        .subtract(orderItemEntity.getIntegrationAmount())
        );
        return orderItemEntity;
    }

}